perm filename POINTY.DOC[PNT,HE]4 blob sn#341260 filedate 1978-03-15 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00014 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002		PURPOSE OF THIS FILE
C00003 00003		IMPLEMENTATION COMMENTS 
C00007 00004		THE PARSER
C00011 00005		THE SCANNER
C00016 00006		SYMBOL TABLE ORGANIZATION
C00023 00007		INITIALIZATIONS
C00027 00008		FILE TABLE ORGANIZATION
C00032 00009		DISPLAY 
C00037 00010		HOW "KILL" IS IMPLEMENTED
C00042 00011		DEFAULT PARTS 
C00045 00012		HOW "CONSTRUCT" IS IMPLEMENTED
C00048 00013		HOW TO HANDLE THE ROTATION MATRIX
C00056 00014		THE EXPRESSION EVALUATOR
C00058 ENDMK
C⊗;
	PURPOSE OF THIS FILE

	This file is a legacy of Maria Gini and Pina Gini left behind
to explain in an informal way the control structure of the final POINTY
system, and meant to help anybody who plans to modify/use POINTY.
The file was originally put together by Maria (whose programmer name is MLG)
whom we should verbally thank each time we use this file instead of
having to chase through half a dozen files trying to figure where is
what.

						Shahid 1-5-78. [MSM]
	IMPLEMENTATION COMMENTS 

Some comments about some implementation choices are in next pages.


The program is in the file POINTY.SAI[PNT,HE].
It requires some source files and load modules
	
conditional		source file				load module
flag
			MAINPR.SAI[PNT,HE]
			MACROS.SAI[PNT,HE]
			RECORD.DEF[PNT,HE]

			PARSER.HDR[PNT,HE]			PARSER[PNT,HE]
			OPERAT.HDR[PNT,HE]			OPERAT[PNT,HE]
			OUTPUT.HDR[PNT,HE]			OUTPUT[PNT,HE]
			ARMINT.SAI[PNT,MSM]			TLKF5a[PNT,HE]

#KILL			KILLER.HDR[PNT,HE]			KILLER[PNT,HE]	

#HELP 			HELP.HDR[PNT,HE]			HELP[PNT,HE]

#ARROW								ARROW[PNT,HE]

#DISPL								DISPLY[PNT,HE]

#OUTPT								INPOUT[PNT,HE]

#MOVE							(	TLKF3a[PNT,HE]
							(	MOVARM[PNT,HE]
							(	BEJCZY[PNT,HE]
							(	ARMSOL[PNT,HE]

file		purpose

HELP  		used by HELP.SAI to read the explanations about the syntax.

POINTY.SAI 	outermost file with specified compilation flags for a complete
		version of POINTY.

MACROS.SAI 	useful macro definitions

RECORD.DEF	external declarations for the record classes, the
		symbol table and the pointers to the predefined variables

PARSER.SAI	basic parsing procedures

OPERAT.SAI	basic arithmetic operations

EXPR.SAI	procedures for parsing and calling the arithmetic expressions

OUTPUT.SAI	procedures to construct the string with the decoded
		values of the different variables for printing

DISPLY.SAI	display procedures

INPOUT.SAI	for file output (read is in MAINPR)

KILLER.SAI	procedures required by KILL instruction

HELP.SAI	procedures for the complete error explanations and syntax
		messages

ARROW.SAI	contains procedure for drawing arrow on the display.


**********************
The previous working version of POINTY is POINTY.OLD[PNT,HE].
The dump file is OLD.DMP[PNT,HE].  This was the state of the world
as of end of December, 1977, and meant as backup in case there
are some bugs in the new version.
	THE PARSER

One line is read on tty and is saved in $LINE.  $LINE may contain more (or
less) than one instruction.

For error  messages  another  variable  is  used,  $NEXT.   $NEXT  at  the
beginning is  the  same  as  $LINE.   If  $LINE  contains  more  than  one
instruction, each time an instruction has been parsed $NEXT is updated  to
be the part of $LINE still to  be parsed.  If the instruction in $LINE  is
not complete the  system waits  for a  new line:   in this  case $NEXT  is
updated by attaching the new line. So, no matter how the instructions have
been typed in they are saved in $NEXT for error messages.

$TAIL contains one instruction (if a break character has been found) or  a
part of it. During the parsing process $TAIL is continuously updated to be
the part of the instruction not yet parsed. It'important to notice that if
no break character is found a <CR> is attached at the end of $TAIL. So the
scanner may know when the end of  the instruction or of the line has  been
reached, and operate subsequently.

Different break tables are used by the parser and the scanner.

The general table used in the  main loop to separate one instruction  from
the others  is  $SCNTAB.  Its  specifications are  expressed  in  SAIL  by
SETBREAK ($SCNTAB←GETBREAK,";?{",CR&LF&FF&TV,"INAK");

The break  characters are  ";", which  is the  normal termination  of  the
instruction, "?",  which  is used  for  help instruction  and  "{",  which
indicates the beginning of a comment.
In that last  case a special  break table,  $CMNTAB, is used  to find  the
matching "}"; its specifications are expressed in SAIL by the  instruction
SETBREAK ($CMNTAB←GETBREAK,"}",NULL,"INA");

The parsing is done by the procedure PARSE.

The basic idea in PARSE is to check the first token of the instruction  to
detect what kind  of instruction it  is.  In fact  the instructions  begin
with a  reserved word,  unless they  are assignment  instructions (or  the
reserved word is  mispelled!!).  To do  the checking in  a faster way  the
first letter of the first word is  checked and then a case table is  used.
Then different procedures are called, to parse the different instructions.

(MARCH 1978 - this has been changed by MSM.  Now GTOKEN returns
additional information besides the token itself, and if the token
is a reserved token, PARSE causes it to branch to the right routine
according to the index of the reserved word.)
	THE SCANNER

The procedure  GETTOKEN reads  the  next token  in  the string  $TAIL  and
returns it in TOKEN.  The syntactic type of TOKEN is in #TOKEN.
It is	ID_TYPE  for identifiers,
	INT_TYPE  for integer numbers,
	REAL_TYPE  for real numbers,
	OPERATOR_TYPE  for punctuation marks, operators..
	UNDECLARED_TYPE for undeclared variables,
	RESERVED_TYPE for reserved words.

GETTOKEN uses different break tables

BTABLE←".,;[]()+-*/←↑↓→?α$"&LF&CR&TAB&FF&SP;

SETBREAK ($RETAB ←GETBREAK,BTABLE,NULL,"INR");	
is used to read the token, retaining the break character 
SETBREAK ($SKTAB ←GETBREAK,BTABLE,NULL,"INS");
is used to  read the  token after  using $RETAB,  just to  skip the  break
character left before in the string
SETBREAK ($SPCTAB←GETBREAK,TAB&SP,NULL, "XNR");
is used to skip the blanks before the token
SETBREAK ($ALFTAB←GETBREAK,NULL,NULL,"XRN");
is used to read one character from the string, usually to check if it's  a
number or not
SETBREAK ($NUMTAB←GETBREAK,"@+-0123456789",NULL,"XNR");	! as table 10;
is used while reading a number to detect the first non-number character

The idea  of GETTOKEN  is  to skip  the blanks,  then  to read  the  token
retaining the break character. The action is then dictate by the token and
the break.  If the token is null there are two cases: can be a number  (as
.12@3) or simply a punctuation mark(as . or ,) or the end of the string in
one instruction  requiring  more  typing(instruction  typed  in  different
lines).  So  these  cases  are  identified taking  a  look  at  the  break
character and deciding either to wait for more input (break=<cr> and  flag
indicating the instruction is not terminated), or to return a  punctuation
mark or again to continue reading a number. If the break character is a  .
two cases are possible, because it can be part of a floating number or can
be only a punctuation mark.
If the token read  at the beginning  is not null  two different cases  are
possible: the break is a . or is a different character so the token is  an
identifier.
In the first case (break .) the string  is scanned to see if the token  is
composed only by numbers (in that case is a floating number and other more
numbers can be present after  .) or if the  token is simply an  identifier
followed by a .
If after  this process  the token  results to  be an  identifier there  is
another check to  be done, because  it can  be an integer  number. So  the
token is scanned again, looking  at the first character  to see if it's  a
number.  In  that case  the other  characters are  checked to  define  the
correct type of the token.


Other break tables are used for different purposes.

* $DSTTAB  is used  by COPY/MERGE  instructions  to find  if there  is  an
underscore in the name of the frames.
* $ERRTAB is used by the recover procedure to check if the new name  typed
is an identifier. If a break  character is found the recover will  proceed
asking to type again a correct isdentifier. During the scanning spaces and
<cr>'s are eliminated.
* $BSKTAB is used for the display to compact the  output, by  erasing  the
spaces.
	SYMBOL TABLE ORGANIZATION
			
The symbol table  is constituted by  an array $YMTAB,  whose elements  are
rptr's to records of class SYMBOL.
	* the number of positions in $YMTAB is #LMT+1, with #LMT=499.
The symbol table is then divided in parts, one for each type of variables.
	* the number of the types is #NTYPE, with #NTYPE=#MAX-#MIN+1
	where #MIN = 1 and #MAX = 5.
	They corresponds to SCALAR, VECTOR, ROT, FRAME, TRANS.
	* the number of positions for each type is #LTYPE. It's given by
	#LTYPE= 100.
	* the type SCALAR is in the first part of $YMTAB (#SC=1=#MIN), the 
	type VECTOR in the second (#VT=2), the type ROT in the third (#RT=3), 
	the type TRANS in the fourth (#TR=4) and the type FRAME in the fifth 
	(#FR=5).
	* #SC, #VT, #RT, #FR and #TR are used to enter in the appropriate 
	part of $YMTAB. 

The records of class SYMBOL have two fields
	pname, is the pname of teh symbol (string),
	object, is the rptr to the record of the appropriate class.


The integer array $ENTRY is used to  keep trace of the number of  elements
inserted in $YMTAB.
	* the number of positions in $ENTRY is #NTYPE
	* each position in $ENTRY corresponds to one type of variables
	* each position contains the entry in $YMTAB of the first position
	free for the corresponding type.
	* the range of values for the i-element of $ENTRY (orresponding to all
	the entries in $YMTAB for the class i) is given by
	#LTYPE*(i-#MIN)≤ $ENTRY[i] ≤ (#LTYPE*i)-#MIN

The different types of variables are stored on different types of records.
	* SCALAR 
	  scalar:value contains the real value of the scalar
	* VECTOR
	  vector:xc contains the real value of the component along x-axis
	  vector:yc contains the real value of the component along y-axis
	  vector:zc contains the real value of the component along z-axis
	* ROT
	  rot:xf is a real array[1:5,1:4], as in FRAME
	* FRAME
	  frame:pname contains the pname of the frame (string)
	  frame:dad contains the pointer to its dad in the tree
	  frame:son contains the pointer to its last son
	  frame:ebro contains the pointer to its elder brother
	  frame:ybro contains the pointer to its younger brother
	  frame:howlinked contains the kind of affixment(#RGDLK,#NRGLK,#INDLK)
	  frame:xf is a real arry containing
		xf[1:3,1:3]=rotation matrix,
		xf[1:3,4]=translation vector,
		xf[4,1:3]=0,
		xf[4,4]=1,
		xf[5,1:3]=rotation angles (euler angles)
		xf[5,4]>0 if angles are valid;
	* TRANS
	  trans:xf is a real array [1:5,1:4], as in FRAME



SYMTAB[0:lmt]  	symbol			scalar
______		_______________		_________
|    |          |     |	      |		|       |
|   -|---------→|pname|object-|--------→| value |
|____|          |_____|_______|         |_______|
|    |
|   -|-----→..
|____|
|    |
| .. |					vector
|____|		_______________		__________
|    |          |     |       |         |  |  |  | 
|   -|---------→|pname|object-|--------→|xc|yc|zc|
|____| 		|_____|_______|		|__|__|__|
|    |
|   -|-----→..
|____|
|    |
| .. |					rot
|____|		_______________		____________
|    |		|     |       |		|          |   
|   -|---------→|pname|object-|--------→|xf(array) |
|____|		|_____|_______|		|__________|
|    |        
|   -|-----→...			
|____|
|    |
| .. |					frame
|____|		_______________		______________________________________
|    |		|     |       |		|     |  |   |   |    |    |         |
|   -|---------→|pname|object-|--------→|pname|xf|dad|son|ebro|ybro|howlinked|
|____|		|_____|_______|		|_____|__|_|_|_|_|_|__|_|__|_________|
|    |						   ↓   ↓   ↓    ↓
|   -|-----→..					       frame
|____|
|    |
| .. |					trans
|____|		_______________		____________
|    |		|     |       |		|          |   
|   -|---------→|pname|object-|--------→|xf(array) |
|____|		|_____|_______|		|__________|
|    |        
|   -|-----→...			
|____|
|    |
| .. |				
|____|


There are many predeclared symbols. Their pointers and values are described
in the session about initialization.

Some other pointers are defined, to keep track of some important information,
or to define some variable internally used, as 

	rptr(frame)

	F_ARM, used to remember what arm is holding the pointer, 
	F_FID used to store the rptr to the FIDUCIAL point (when defined)

	rptr(trans)

	ARRAY T_CSTR[1:3] used to store the frames defined by CONSTRUCT 
	without arguments
	INITIALIZATIONS

PROCEDURES REQUIRING TO BE INITIALIZED

	INIBRK
	initializes the break tables
	initializes the string $BLANK
	sets the number of decimal characters for real numbers to 3.
	
	INISYM
	initializes the symbol table.


VARIABLES REQUIRING TO BE INITIALIZED

	$ALFL←"DECLAR.AL"
	default name for input/output file

	$EPS = 0.001
	is a value used for tests while using trigonometric functions

	$READ←FALSE
	used by readcode: true while reading


PREDECLARED POINTY VARIABLES

	the first element is the pname (printname) of the variable
	the second is the record_pointer to the record of class SYMBOL
	the third  is the record_pointer to the record of the particular
	    class (SCALAR,VECTOR,ROT,TRANS,FRAME). These names begin with the
	    initial of the class  name and an underscore
	the fourth is the value. Values for rot, frame and trans are expressed 
	    by six elements (w,ph,th,x,y,z) corresponding to 
	    (rot(zhat,th)*rot(yhat,ph)*rot(zhat,w),vector(x,y,z))

	pname(string)	rptr(symbol)	rptr(scalar)	value

	BHAND		HANDB		S_BHAND	
	YHAND		HANDY		S_YHAND
	DEG		DEG				1
	DEGREE		DEGREE				1
	DEGREES		DEGRES				1
	INCH		INCH				1
	INCHES		INCHES				1

					rptr(vector)

	XHAT		XHAT		V_XHAT    	(1,0,0)
	YHAT		YHAT		V_YHAT    	(0,1,0)
	ZHAT		ZHAT		V_ZHAT    	(0,0,1)
	NILVECT		NILVECT		V_NILVECT	(0,0,0)

					rptr(rot)

	NILROTN		NILROTN		R_NILROTN	(0,0,0) euler angles

					rptr(frame)

	STATION		WORLD		F_WRLD		(0,0,0,0,0,0)
	BPARK		BPARK		F_BPARK		(0,180,0,
							   43.53125,56.855,9.95875)
	YPARK		YPARK		F_YPARK		(0,180,0,40,14,9)
	BARM		BARM		F_BARM   	
	YARM		YARM		F_YARM   	(0,0,0,0,0,0)
	BGRASP		BGRASP		F_BGRASP	(-180,180,0,0,0,0) 
								affixed to BARM
	POINTER		POINTER		F_POINTER	(-.417,13.2,-5.173,
							   .0121,.119,3.75) 
								affixed to BARM

					rptr(trans)

	NILTRANS	NILTRANS	T_NILTRANS	(0,0,0,0,0,0)

******* important notice

If other predefined variables are inserted the values in the array SAVE of
the procedure RESET in MAINPR.SAI have to be accordingly modified;
	FILE TABLE ORGANIZATION

POINTY allows to write on different files, to save or to close them.   The
names of the files used, and their  status are mantained in a file  table,
with an index to know the number of used files.  That table is constituted
by two matrices. The information about one file can be found in those  two
matrices on the same row.
	* $NAMEFL is a string array [1:10], used for the file names
	* $CHNFL  is an integer array [1:10,0:1], containing in the  first 
	column the status open(0)/close(1) of the file and  in the  second 
	column the number of the channel associated. 
	* $TOTFL is  an integer,  whose value is the total number of files 
	used.


		   $NAMEFL                $CHNFL
		 ______________    __________________________
		 |name of file|    |open/close |  channel # |
		 |(string)    |    | 0     1   |            |
		 |____________|    |___________|____________|
            	 |            |    |           |            |
		 |	      |    |           |            |

The last file used for output is the default name for output  instruction.
We'll call this "current file".
	* $ALFL, is a string containing the name of the current file 
	(initialized with DECLAR.AL). When the current file is closed the 
	name of a previously used open file is taken as  default, or,  if 
	there are'nt any DECLAR.AL is taken. The current file is indicated 
	on the display by a * before the file name.
	* $ALCH is the channel number of the current file.


The output of TTY can be saved in  a file. The name of this file is  given
at the start of the  session, and can be  changed only by teh  instruction
CLOSE_FILES.  In the case the TTY output has to be recorded same variables
are used
	* $OUT, is used as a flag to know if the output has to be saved 
	* $TTYFL is the name of the file
	* $TTYCH is the channel number.

The input in TTY is converted in upper cases, so lower or upper cases  can
be used  in any  combination. There  are problems  only while  recovering,
because the characters typed into the line editor are not converted.


For READ instruction the global variables used are
	* $READ, used as flag to know if teh instruction being executed is 
	a READ instruction. The  flag  is  required to  continue  with the 
	reading in the case the file contains an error, since  each  error
	causes a goto to the main loop
	* $INPCH is the number of the channel associated  with  the  input 
	file. It'used to remember the channel number after an  error,  and 
	it's also used by SAVE instruction, when the  saved  file is  read 
	and copied.
	DISPLAY 


Some of the procedures for the display are in DISPLY.SAI[1,MLG].
The global variables used in that file are prefixed by ∂.
That file requires

source file					load_module
DPYSUB.HDR[SUB,SYS]     DPYSUB.SAI[SUB,SYS]	DPYSUB[SUB,SYS]


The procedure INIDPY  initializes the display,  assigning the  appropriate
values to the variables defining margin positions.

     ∂DLMAR		       ∂SCFR  ∂DRMAR
	↓			 ↓	↓
	_________________________________
∂DTMAR→	|		  	|	|
	|			|	|
	|  FRAME TREE		| SC	|
	|  TRANS		|-------| ← ∂SCDF
∂TRFL→	|_______________________|_______|
	|	   |	      |  	|
	| FILE	   |  ROT     |  VT	|
	|__________|__________|_________|
∂TPMAR→ 

                   ↑          ↑
	         ∂FLRT      ∂RTVT      
∂DBMAR →

The internal/external variables are

	$ARROW				position of the arrow
		external integer
	$BLANK				string of blanks
		external string 
	$BRCHR				break character
		external integer 
	$BSKTAB				break table used to get ride of blanks
		external integer 
	$DPYTAB				breaktable used to split the string 
		external integer	in different lines)
  	$EPS
		external real 
	$NCHAR				number of characters for frame space
		internal integer	(=∂wfr/∂chwid)

The external procedure used is

	EULERO(REAL ARRAY XF;REFERENCE REAL W,PH,TH);


The global variables of DISPLY.SAI are

	∂BUF				buffer for the display
		integer array [1:1000]
	∂CHWID 				width of a character(=15 on DD,12 on III)
	∂CHIGH 				height of a line (=20)
	∂DBMAR				bottom margin of the display (=-510 on DD,
					-450 on III)
	∂DLMAR				left margin (=-625 on DD,-510 on III)
	∂DRMAR				right margin (=580 on DD, 510 on III)
	∂DTMAR				top margin (=450)
	∂DWNLNS 			number of lines for lower part of display
					(=12)
	∂FLRT 				margin between files and rot's
					(=-330 on DD,-215 on III)
	∂RTVT 				margin between rot's and vectors
					(=175 on DD, 147 on III)
	∂SCDF 				margin between defaults and scalars(=-10)
	∂SCFR 				margin between frames and scalars
					(=400 ON DD,330 ON III)
	∂SIZE 				size of the characters (=2)
	∂TPMAR 				typing space top margin (=-318 on DD,
					-270 on III)
	∂TRFL 				trans's bottom margin (=-70)
	∂UPLNS				number of lines in upper part of display
					(= 26)
	∂WFR 				width of space for frame tree 
					(=1015 on DD,830 on III)
	∂WRTVT 				width of space for vectors and rot's
					(=495 on DD,352 on III)
	∂WSC 				width of space for scalars (=170)


The procedure ARROW draws an arrow on the screen


		 .	80	   .  20   .			
        c3y	 ..................3.....................
	         .                 |\ 	   .		10
 	c12y    1 ________________2|  \ .................
	   	 |		   .	\  .
	c4y	 |		   .	  \4		20
		 |		   .	  /.
		 |__________________    /................
 	c67y    7.                6|  /    .		10
 	c5y	 ..................|/....................
		 .		   5       .
		 .		   .       .

	        c17x              c2356x   c4x		;

It's possible to move by one line the array with the instruction ↑ , ↓ than can 
be preceded by any integer number.
	HOW "KILL" IS IMPLEMENTED

The kill instruction allows  to delete the last  instruction and its  side
effects.  Each time an  instruction is parsed the  kill is initialized  by
INIKIL, which sets  to null_record the  record pointer to  the record  (or
list of records) used  to save the information  about the variables  being
modified. That record pointer is KILL.
With that  implementation  would  be  very easy  to  save  more  than  one
instruction, allowing so to delete more than the last one. It's enough  to
save not only  one record  pointer (for  the last  instruction), but  more
record pointers (in a  circular list)and for  each instruction delete  the
oldest and insert the new one.

The records used are of class SAVED, where
	saved:addr is the address (row number) in $YMTAB of the symbol
		modified.
	saved:type is the type of that symbol (#sc,#vt,#rt,#fr,#tr) or
		is a special type #nw, for new symbols, or #nwfr, for
		new frames. That choice ease the handling of the record.
	saved:symbol is the record pointer to the record of class symbol.
		If the symbol is a new defined one is null_record.
	saved:object is the record pointer to the record of one type (#sc,
		#vt,#rt,#fr,#tr) if the record is created in the same
		instruction (so the type is #nw or #nwfr) or to the record 
		used to copy the value 	of the modified variable.
	saved:dad is the record pointer to the dad of the frame (used only
		when tree modifications are involved).
	saved:link is the kind of affixment (same as before).
	saved:next is the pointer to next record of the same type. All
		the records used to save information about one POINTY 
		instruction are connected by this link, so it's possible
		to follow that chain to restore the previous situation.

How to save the information

There are two main procedures to save the status of a variable
	SAVNEW, saves the status of a variable being defined in the instruction
	itself. In that case saved:addr is taken from $ENTRY, and saved:object
	is the rptr to the symbol. So if the instruction is killed is enough
	to delete the symbol from $YMTAB.
	SAVOLD, saves the status of a variable previously defined which
	is modified in the instruction. A variable is used to remember the 
	address in $YMTAB of teh last checked symbol, $ROW. Its value is 
	inserted in saved:addr. Then a new record is constructed

How to recover

To know what was the last instruction a variable $LAST is used. The different
types of instructions are

	kil, for not killable instructions
	decl, for declarations
	del, for deletion
	asg, for assignment
	afx, for affix/unfix
	cpy, for copy and merge


	DEFAULT PARTS 

POINTY provides many default parts in different instructions.

For the instructions of movement there  is a part of the instruction  that
can be left out. The system  remembers the last movement instruction  used
and supply the first part of it as default. That part is displayed on  the
little box below the scalar box on the display.
Two global variables are used to store that default part (OLDCMD and OLDOBJ).


default part			part of the instruction to be typed in
____________________________________________________________________________________
OLDCMD   OLDOBJ
____________________________________________________________________________________

MOVE	 <frame_id>		BY <vector>
				TO <frame_id> {+<vector> {WRT <frame_id>}}

MOVEX	 {<frame_id>}		BY <scalar>
MOVEY	 {<frame_id>}		BY <scalar>
MOVEZ	 {<frame_id>}		BY <scalar>

OPEN	 <hand>			TO <scalar>
				BY <scalar>

CLOSE	 <hand>			TO <scalar>
				BY <scalar>

DRIVE	  BJT(<number>)		TO <scalar>
				BY <scalar>


Other instructions allow a default part

instruction			default
___________________________________________________________________________________

BARM←				INPUT BARM	(to update arm position)
CENTER				BARM
CLOSE				current file
<id>←INPUT			POINTER
YARM←				INPUT YARM  	(to update arm position)
OPEN TO|BY <scalar>		BHAND 
MOVEX BY <scalar>		BARM
MOVEY BY <scalar>		BARM
MOVEZ BY <scalar>		BARM
READ				DECLAR.AL
SAVE				current file
WRITE				current file FROM STATION
WRITE FROM <frame_id>		current file
WRITE <file>			FROM STATION

vector, rot....			*INCH, *INCHES, *DEG, *DEGREES	(no dimension check)

	HOW "CONSTRUCT" IS IMPLEMENTED

Three positions are required to define a reference system:
	at the origin
	on one main axis 
	on the plane between the previous axis and another main axis

So, given three vectors v1, v2 and v3, and knowing
	v1	is at the origin of the frame
	v2	is on the axis first_axis (f_axis)
	v3	is on the plane through f_axis and second_axis (s_axis)
we want to determine the third_axis (t_axis).

We'll associate an integer number  to each one of  the main axes, to  ease
the computations
	xhat	1
	yhat	2
	zhat	3
Those numbers relates the name of the axis to the number of the column  in
the rotation matrix corresponding to it.

	* the vector v1 gives the position of the new frame 
		(column 4 of the rotation matrix);
	* the vector |v2-v1| gives the f_axis 
		(columm f_axis of the rotation matrix);

To determine the  other two  main axes (column  s_axis and  t_axis of  the
rotation matrix) there are two different cases:

a)				   	b)
	↑ f_axis		     		↑ f_axis
	v2					v2
v3    	|					|	v3
	|					|
	v1-----------→	t_axis			v1------------→	s_axis
      /					      /
s_axis					t_axis
  /					  /


t_axis = ||v2-v1|*|v3-v1|| 		t_axis = ||v3-v1|*|v2-v1||
s_axis = t_axis * f_axis		s_axis = f_axis * t_axis

To determine what is the appropriate  case it's enough to check if  s_axis
follows f_axis in the regular order (case a) or not (case b). This  check,
using  the  numbers  associated  to  the   axes,  is  a  simple  test   on
permutations.
	HOW TO HANDLE THE ROTATION MATRIX

POINTY uses a rotation matrix [1:5,1:4], 
	the part [1:3,1,3] is the rotation matrix
	the part [1:3,4]   is the translation part
	the part [4,1:4]   is used only to ease the operations
	the part [5,1:3]   contains the euler angles corresponding to the rot
	the element [5,4]  is >0 when the euler angles are valid

!!!!!!! WARNING the rotation matrix [1:3,1:3] used by POINTY is the transpose
of the rotation matrix used by the interface and by AL.

	* how to compute the rotation matrix

The procedure XYZROT used to compute  the rotation matrix, for a  rotation
of an angle W about the axis V (with components CX, CY, CZ about the  main
axes ) constructs the matrix with the following formulas

MATRIX ROT(3,3)$       
	ROT(1,1) :=  - COS(W)*CX**2  + COS(W) + CX**2$
	ROT(1,2) :=  - COS(W)*CX*CY + CX*CY - CZ*SIN(W)$
	ROT(1,3) :=  - COS(W)*CX*CZ + CX*CZ + CY*SIN(W)$
	ROT(2,1) :=  - COS(W)*CX*CY + CX*CY + CZ*SIN(W)$
	ROT(2,2) :=  - COS(W)*CY**2  + COS(W) + CY**2$
	ROT(2,3) :=  - COS(W)*CY*CZ - CX*SIN(W) + CY*CZ$
	ROT(3,1) :=  - COS(W)*CX*CZ + CX*CZ - CY*SIN(W)$
	ROT(3,2) :=  - COS(W)*CY*CZ + CX*SIN(W) + CY*CZ$
	ROT(3,3) :=  - COS(W)*CZ**2  + COS(W) + CZ**2$

(the previous rows are in a form readable by REDUCE 2)

The procedure  SETROT is  used  to compute  directly the  rotation  matrix
corresponding to  ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W)  given  the  Euler
angles W, PH and TH (see next section).


	* how to decode the rotation matrix

Usually the matrix is decoded computing the Euler angles.
To show how the decode process is done we'll show the symbolic product  of
three rotations
	ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W)
(the interpretation of the product between rotations is the same as in AL)

given the three rotation matrices

MATRIX MATZTH(3,3);		COMMENT ROT(ZHAT,TH);
	MATZTH(1,1) := COS(TH);
	MATZTH(1,2) :=  - SIN(TH);
	MATZTH(1,3) := 0;
	MATZTH(2,1) := SIN(TH);
	MATZTH(2,2) := COS(TH);
	MATZTH(2,3) := 0;
	MATZTH(3,1) := 0;
	MATZTH(3,2) := 0;
	MATZTH(3,3) := 1;
MATRIX MATZPH(3,3);		COMMENT ROT(YHAT,PH);
	MATYPH(1,1) := COS(PH);
	MATYPH(1,2) := 0;
	MATYPH(1,3) := SIN(PH);
	MATYPH(2,1) := 0;
	MATYPH(2,2) := 1;
	MATYPH(2,3) := 0;
	MATYPH(3,1) :=  - SIN(PH);
	MATYPH(3,2) := 0;
	MATYPH(3,3) := COS(PH);
MATRIX MATZW(3,3);		COMMENT ROT(ZHAT,W);
	MATZW(1,1) := COS(W);
	MATZW(1,2) :=  - SIN(W);
	MATZW(1,3) := 0;
	MATZW(2,1) := SIN(W);
	MATZW(2,2) := COS(W);
	MATZW(2,3) := 0;
	MATZW(3,1) := 0;
	MATZW(3,2) := 0;
	MATZW(3,3) := 1;
we'll obtain the product
MATRIX EULER(3,3);		COMMENT ROT(ZHAT,TH)*ROT(YHAT,PH)*ROT(ZHAT,W);
	EULER(1,1) := COS(W)*COS(TH)*COS(PH) - SIN(W)*SIN(TH);
	EULER(1,2) :=  - (COS(W)*SIN(TH) + SIN(W)*COS(TH)*COS(PH));
	EULER(1,3) := COS(TH)*SIN(PH);
	EULER(2,1) := COS(W)*SIN(TH)*COS(PH) + SIN(W)*COS(TH);
	EULER(2,2) := COS(W)*COS(TH) - SIN(W)*SIN(TH)*COS(PH);
	EULER(2,3) := SIN(TH)*SIN(PH);
	EULER(3,1) :=  - COS(W)*SIN(PH);
	EULER(3,2) := SIN(W)*SIN(PH);
	EULER(3,3) := COS(PH);

The procedure  EULERO extract from that matrix the angles W, PH, TH.

Sometimes we need to  decode the rotation matrix  in a different way.  For
example to construct a matrix corresponding to the arm position, but  with
the ZHAT pointing up vertically, we need to decode the matrix as a product
of rotations about the three main axes, and then to use only the component
of the rotation about ZHAT.

To show how the decode process is done we'll show the symbolic product  of
three rotations
	ROT(ZHAT,C)*ROT(YHAT,B)*ROT(ZHAT,A)
(the interpretation of the product between rotations is the same as in AL)

MATRIX MATX(3,3);		COMMENT ROT(XHAT,A);
	MATX(1,1) := 1;
	MATX(1,2) := 0;
	MATX(1,3) := 0;
	MATX(2,1) := 0;
	MATX(2,2) := COS(A);
	MATX(2,3) :=  - SIN(A);
	MATX(3,1) := 0;
	MATX(3,2) := SIN(A);
	MATX(3,3) := COS(A);
MATRIX MATY(3,3);		COMMENT ROT(YHAT,B);
	MATY(1,1) := COS(B);
	MATY(1,2) := 0;
	MATY(1,3) := SIN(B);
	MATY(2,1) := 0;
	MATY(2,2) := 1;
	MATY(2,3) := 0;
	MATY(3,1) :=  - SIN(B);
	MATY(3,2) := 0;
	MATY(3,3) := COS(B);
MATRIX MATZ(3,3);		COMMENT ROT(ZHAT,C);
	MATZ(1,1) := COS(C);
	MATZ(1,2) :=  - SIN(C);
	MATZ(1,3) := 0;
	MATZ(2,1) := SIN(C);
	MATZ(2,2) := COS(C);
	MATZ(2,3) := 0;
	MATZ(3,1) := 0;
	MATZ(3,2) := 0;
	MATZ(3,3) := 1;
we'll obtain the product
MATRIX MATPROD(3,3);		COMMENT ROT(ZHAT,C)*ROT(YHAT,B)*ROT(XHAT,A);
	MATPROD(1,1) := COS(C)*COS(B);
	MATPROD(1,2) := SIN(A)*COS(C)*SIN(B) - SIN(C)*COS(A);
	MATPROD(1,3) := SIN(A)*SIN(C) + COS(C)*SIN(B)*COS(A);
	MATPROD(2,1) := SIN(C)*COS(B);
	MATPROD(2,2) := SIN(A)*SIN(C)*SIN(B) + COS(C)*COS(A);
	MATPROD(2,3) :=  - SIN(A)*COS(C) + SIN(C)*SIN(B)*COS(A);
	MATPROD(3,1) :=  - SIN(B);
	MATPROD(3,2) := SIN(A)*COS(B);
	MATPROD(3,3) := COS(B)*COS(A);

The procedure DECODE decodes  the matrix computing the  three angles A,  B
and C.  When the angle B is 90 degrees it's impossible to compute A and C,
but it's only possible to  compute their difference. The procedure  DECODE
forces A to 0 and returns in C the value of (C-A).
	THE EXPRESSION EVALUATOR

	The expression evaluator is a top down single stage evaluator.
It tries to perform computations as soon as something is in a stage
that can be evaluated.  No explicit tree of the parsed expression
is built up - an implicit tree is formed by means of the three recursive
routines that evaluate expressions, terms and factors.
The syntax of the three are as follows:

	expressions	E:	<+ T | - T | T > {+T|-T}
					
	term		T:	F { * F | / F }

	factor		F:	( E , E, E,... ) or | E |
				or func(E,...)
				or E WRT E
				or E REL E

	The expression evaluator (invoked by GTEXPR)
returns a record pointer to a record
of class TREE which have fields DATA and DTYPE.
DATA is a record pointer field of class SCALAR,VECTOR,TRANS,ROT or FRAME
DTYPE is an integer which indicates which record class DATA corresponds to.

	The expression evaluator calls the following procedures:

	GTOKEN	which returns the next symbol and its type
	OPSCAL,OPVET,etc	which perform the actual arithmetic
	ERROR	which is defined by POINTY and is the routine for
		handling error messages.  Since ERROR does not return		
		control to the expression evaluator, continuation
		of computation is started afresh with the corrected
		expression.